package messages

// Reference: https://www.ietf.org/rfc/rfc4120.txt
// Section: 5.4.2

import (
	
	

	
	
	
	
	
	
	
	
	
	
	
	
)

type marshalKDCRep struct {
	PVNO    int                  `asn1:"explicit,tag:0"`
	MsgType int                  `asn1:"explicit,tag:1"`
	PAData  types.PADataSequence `asn1:"explicit,optional,tag:2"`
	CRealm  string               `asn1:"generalstring,explicit,tag:3"`
	CName   types.PrincipalName  `asn1:"explicit,tag:4"`
	// Ticket needs to be a raw value as it is wrapped in an APPLICATION tag
	Ticket  asn1.RawValue       `asn1:"explicit,tag:5"`
	EncPart types.EncryptedData `asn1:"explicit,tag:6"`
}

// KDCRepFields represents the KRB_KDC_REP fields.
type KDCRepFields struct {
	PVNO             int
	MsgType          int
	PAData           []types.PAData
	CRealm           string
	CName            types.PrincipalName
	Ticket           Ticket
	EncPart          types.EncryptedData
	DecryptedEncPart EncKDCRepPart
}

// ASRep implements RFC 4120 KRB_AS_REP: https://tools.ietf.org/html/rfc4120#section-5.4.2.
type ASRep struct {
	KDCRepFields
}

// TGSRep implements RFC 4120 KRB_TGS_REP: https://tools.ietf.org/html/rfc4120#section-5.4.2.
type TGSRep struct {
	KDCRepFields
}

// EncKDCRepPart is the encrypted part of KRB_KDC_REP.
type EncKDCRepPart struct {
	Key           types.EncryptionKey  `asn1:"explicit,tag:0"`
	LastReqs      []LastReq            `asn1:"explicit,tag:1"`
	Nonce         int                  `asn1:"explicit,tag:2"`
	KeyExpiration time.Time            `asn1:"generalized,explicit,optional,tag:3"`
	Flags         asn1.BitString       `asn1:"explicit,tag:4"`
	AuthTime      time.Time            `asn1:"generalized,explicit,tag:5"`
	StartTime     time.Time            `asn1:"generalized,explicit,optional,tag:6"`
	EndTime       time.Time            `asn1:"generalized,explicit,tag:7"`
	RenewTill     time.Time            `asn1:"generalized,explicit,optional,tag:8"`
	SRealm        string               `asn1:"generalstring,explicit,tag:9"`
	SName         types.PrincipalName  `asn1:"explicit,tag:10"`
	CAddr         []types.HostAddress  `asn1:"explicit,optional,tag:11"`
	EncPAData     types.PADataSequence `asn1:"explicit,optional,tag:12"`
}

// LastReq part of KRB_KDC_REP.
type LastReq struct {
	LRType  int32     `asn1:"explicit,tag:0"`
	LRValue time.Time `asn1:"generalized,explicit,tag:1"`
}

// Unmarshal bytes b into the ASRep struct.
func ( *ASRep) ( []byte) error {
	var  marshalKDCRep
	,  := asn1.UnmarshalWithParams(, &, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.ASREP))
	if  != nil {
		return processUnmarshalReplyError(, )
	}
	if .MsgType != msgtype.KRB_AS_REP {
		return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate an AS_REP. Expected: %v; Actual: %v", msgtype.KRB_AS_REP, .MsgType)
	}
	//Process the raw ticket within
	,  := unmarshalTicket(.Ticket.Bytes)
	if  != nil {
		return krberror.Errorf(, krberror.EncodingError, "error unmarshaling Ticket within AS_REP")
	}
	.KDCRepFields = KDCRepFields{
		PVNO:    .PVNO,
		MsgType: .MsgType,
		PAData:  .PAData,
		CRealm:  .CRealm,
		CName:   .CName,
		Ticket:  ,
		EncPart: .EncPart,
	}
	return nil
}

// Marshal ASRep struct.
func ( *ASRep) () ([]byte, error) {
	 := marshalKDCRep{
		PVNO:    .PVNO,
		MsgType: .MsgType,
		PAData:  .PAData,
		CRealm:  .CRealm,
		CName:   .CName,
		EncPart: .EncPart,
	}
	,  := .Ticket.Marshal()
	if  != nil {
		return []byte{}, 
	}
	.Ticket = asn1.RawValue{
		Class:      asn1.ClassContextSpecific,
		IsCompound: true,
		Tag:        5,
		Bytes:      ,
	}
	,  := asn1.Marshal()
	if  != nil {
		return , krberror.Errorf(, krberror.EncodingError, "error marshaling AS_REP")
	}
	 = asn1tools.AddASNAppTag(, asnAppTag.ASREP)
	return , nil
}

// Unmarshal bytes b into the TGSRep struct.
func ( *TGSRep) ( []byte) error {
	var  marshalKDCRep
	,  := asn1.UnmarshalWithParams(, &, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.TGSREP))
	if  != nil {
		return processUnmarshalReplyError(, )
	}
	if .MsgType != msgtype.KRB_TGS_REP {
		return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate an TGS_REP. Expected: %v; Actual: %v", msgtype.KRB_TGS_REP, .MsgType)
	}
	//Process the raw ticket within
	,  := unmarshalTicket(.Ticket.Bytes)
	if  != nil {
		return krberror.Errorf(, krberror.EncodingError, "error unmarshaling Ticket within TGS_REP")
	}
	.KDCRepFields = KDCRepFields{
		PVNO:    .PVNO,
		MsgType: .MsgType,
		PAData:  .PAData,
		CRealm:  .CRealm,
		CName:   .CName,
		Ticket:  ,
		EncPart: .EncPart,
	}
	return nil
}

// Marshal TGSRep struct.
func ( *TGSRep) () ([]byte, error) {
	 := marshalKDCRep{
		PVNO:    .PVNO,
		MsgType: .MsgType,
		PAData:  .PAData,
		CRealm:  .CRealm,
		CName:   .CName,
		EncPart: .EncPart,
	}
	,  := .Ticket.Marshal()
	if  != nil {
		return []byte{}, 
	}
	.Ticket = asn1.RawValue{
		Class:      asn1.ClassContextSpecific,
		IsCompound: true,
		Tag:        5,
		Bytes:      ,
	}
	,  := asn1.Marshal()
	if  != nil {
		return , krberror.Errorf(, krberror.EncodingError, "error marshaling TGS_REP")
	}
	 = asn1tools.AddASNAppTag(, asnAppTag.TGSREP)
	return , nil
}

// Unmarshal bytes b into encrypted part of KRB_KDC_REP.
func ( *EncKDCRepPart) ( []byte) error {
	,  := asn1.UnmarshalWithParams(, , fmt.Sprintf("application,explicit,tag:%v", asnAppTag.EncASRepPart))
	if  != nil {
		// Try using tag 26
		// Ref: RFC 4120 - mentions that some implementations use application tag number 26 wether or not the reply is
		// a AS-REP or a TGS-REP.
		_,  = asn1.UnmarshalWithParams(, , fmt.Sprintf("application,explicit,tag:%v", asnAppTag.EncTGSRepPart))
		if  != nil {
			return krberror.Errorf(, krberror.EncodingError, "error unmarshaling encrypted part within KDC_REP")
		}
	}
	return nil
}

// Marshal encrypted part of KRB_KDC_REP.
func ( *EncKDCRepPart) () ([]byte, error) {
	,  := asn1.Marshal(*)
	if  != nil {
		return , krberror.Errorf(, krberror.EncodingError, "marshaling error of AS_REP encpart")
	}
	 = asn1tools.AddASNAppTag(, asnAppTag.EncASRepPart)
	return , nil
}

// DecryptEncPart decrypts the encrypted part of an AS_REP.
func ( *ASRep) ( *credentials.Credentials) (types.EncryptionKey, error) {
	var  types.EncryptionKey
	var  error
	if .HasKeytab() {
		, _,  = .Keytab().GetEncryptionKey(.CName, .CRealm, .EncPart.KVNO, .EncPart.EType)
		if  != nil {
			return , krberror.Errorf(, krberror.DecryptingError, "error decrypting AS_REP encrypted part")
		}
	}
	if .HasPassword() {
		, _,  = crypto.GetKeyFromPassword(.Password(), .CName, .CRealm, .EncPart.EType, .PAData)
		if  != nil {
			return , krberror.Errorf(, krberror.DecryptingError, "error decrypting AS_REP encrypted part")
		}
	}
	if !.HasKeytab() && !.HasPassword() {
		return , krberror.NewErrorf(krberror.DecryptingError, "no secret available in credentials to perform decryption of AS_REP encrypted part")
	}
	,  := crypto.DecryptEncPart(.EncPart, , keyusage.AS_REP_ENCPART)
	if  != nil {
		return , krberror.Errorf(, krberror.DecryptingError, "error decrypting AS_REP encrypted part")
	}
	var  EncKDCRepPart
	 = .Unmarshal()
	if  != nil {
		return , krberror.Errorf(, krberror.EncodingError, "error unmarshaling decrypted encpart of AS_REP")
	}
	.DecryptedEncPart = 
	return , nil
}

// Verify checks the validity of AS_REP message.
func ( *ASRep) ( *config.Config,  *credentials.Credentials,  ASReq) (bool, error) {
	//Ref RFC 4120 Section 3.1.5
	if !.CName.Equal(.ReqBody.CName) {
		return false, krberror.NewErrorf(krberror.KRBMsgError, "CName in response does not match what was requested. Requested: %+v; Reply: %+v", .ReqBody.CName, .CName)
	}
	if .CRealm != .ReqBody.Realm {
		return false, krberror.NewErrorf(krberror.KRBMsgError, "CRealm in response does not match what was requested. Requested: %s; Reply: %s", .ReqBody.Realm, .CRealm)
	}
	,  := .DecryptEncPart()
	if  != nil {
		return false, krberror.Errorf(, krberror.DecryptingError, "error decrypting EncPart of AS_REP")
	}
	if .DecryptedEncPart.Nonce != .ReqBody.Nonce {
		return false, krberror.NewErrorf(krberror.KRBMsgError, "possible replay attack, nonce in response does not match that in request")
	}
	if !.DecryptedEncPart.SName.Equal(.ReqBody.SName) {
		return false, krberror.NewErrorf(krberror.KRBMsgError, "SName in response does not match what was requested. Requested: %v; Reply: %v", .ReqBody.SName, .DecryptedEncPart.SName)
	}
	if .DecryptedEncPart.SRealm != .ReqBody.Realm {
		return false, krberror.NewErrorf(krberror.KRBMsgError, "SRealm in response does not match what was requested. Requested: %s; Reply: %s", .ReqBody.Realm, .DecryptedEncPart.SRealm)
	}
	if len(.ReqBody.Addresses) > 0 {
		if !types.HostAddressesEqual(.DecryptedEncPart.CAddr, .ReqBody.Addresses) {
			return false, krberror.NewErrorf(krberror.KRBMsgError, "addresses listed in the AS_REP does not match those listed in the AS_REQ")
		}
	}
	 := time.Now().UTC()
	if .Sub(.DecryptedEncPart.AuthTime) > .LibDefaults.Clockskew || .DecryptedEncPart.AuthTime.Sub() > .LibDefaults.Clockskew {
		return false, krberror.NewErrorf(krberror.KRBMsgError, "clock skew with KDC too large. Greater than %v seconds", .LibDefaults.Clockskew.Seconds())
	}
	// RFC 6806 https://tools.ietf.org/html/rfc6806.html#section-11
	if .PAData.Contains(patype.PA_REQ_ENC_PA_REP) && types.IsFlagSet(&.DecryptedEncPart.Flags, flags.EncPARep) {
		if len(.DecryptedEncPart.EncPAData) < 2 || !.DecryptedEncPart.EncPAData.Contains(patype.PA_FX_FAST) {
			return false, krberror.NewErrorf(krberror.KRBMsgError, "KDC did not respond appropriately to FAST negotiation")
		}
		for ,  := range .DecryptedEncPart.EncPAData {
			if .PADataType == patype.PA_REQ_ENC_PA_REP {
				var  types.PAReqEncPARep
				 := .Unmarshal(.PADataValue)
				if  != nil {
					return false, krberror.Errorf(, krberror.EncodingError, "KDC FAST negotiation response error, could not unmarshal PA_REQ_ENC_PA_REP")
				}
				,  := crypto.GetChksumEtype(.ChksumType)
				if  != nil {
					return false, krberror.Errorf(, krberror.ChksumError, "KDC FAST negotiation response error")
				}
				,  := .Marshal()
				if !.VerifyChecksum(.KeyValue, , .Chksum, keyusage.KEY_USAGE_AS_REQ) {
					return false, krberror.Errorf(, krberror.ChksumError, "KDC FAST negotiation response checksum invalid")
				}
			}
		}
	}
	return true, nil
}

// DecryptEncPart decrypts the encrypted part of an TGS_REP.
func ( *TGSRep) ( types.EncryptionKey) error {
	,  := crypto.DecryptEncPart(.EncPart, , keyusage.TGS_REP_ENCPART_SESSION_KEY)
	if  != nil {
		return krberror.Errorf(, krberror.DecryptingError, "error decrypting TGS_REP EncPart")
	}
	var  EncKDCRepPart
	 = .Unmarshal()
	if  != nil {
		return krberror.Errorf(, krberror.EncodingError, "error unmarshaling encrypted part")
	}
	.DecryptedEncPart = 
	return nil
}

// Verify checks the validity of the TGS_REP message.
func ( *TGSRep) ( *config.Config,  TGSReq) (bool, error) {
	if !.CName.Equal(.ReqBody.CName) {
		return false, krberror.NewErrorf(krberror.KRBMsgError, "CName in response does not match what was requested. Requested: %+v; Reply: %+v", .ReqBody.CName, .CName)
	}
	if .Ticket.Realm != .ReqBody.Realm {
		return false, krberror.NewErrorf(krberror.KRBMsgError, "realm in response ticket does not match what was requested. Requested: %s; Reply: %s", .ReqBody.Realm, .Ticket.Realm)
	}
	if .DecryptedEncPart.Nonce != .ReqBody.Nonce {
		return false, krberror.NewErrorf(krberror.KRBMsgError, "possible replay attack, nonce in response does not match that in request")
	}
	//if k.Ticket.SName.NameType != tgsReq.ReqBody.SName.NameType || k.Ticket.SName.NameString == nil {
	//	return false, krberror.NewErrorf(krberror.KRBMsgError, "SName in response ticket does not match what was requested. Requested: %v; Reply: %v", tgsReq.ReqBody.SName, k.Ticket.SName)
	//}
	//for i := range k.Ticket.SName.NameString {
	//	if k.Ticket.SName.NameString[i] != tgsReq.ReqBody.SName.NameString[i] {
	//		return false, krberror.NewErrorf(krberror.KRBMsgError, "SName in response ticket does not match what was requested. Requested: %+v; Reply: %+v", tgsReq.ReqBody.SName, k.Ticket.SName)
	//	}
	//}
	//if k.DecryptedEncPart.SName.NameType != tgsReq.ReqBody.SName.NameType || k.DecryptedEncPart.SName.NameString == nil {
	//	return false, krberror.NewErrorf(krberror.KRBMsgError, "SName in response does not match what was requested. Requested: %v; Reply: %v", tgsReq.ReqBody.SName, k.DecryptedEncPart.SName)
	//}
	//for i := range k.DecryptedEncPart.SName.NameString {
	//	if k.DecryptedEncPart.SName.NameString[i] != tgsReq.ReqBody.SName.NameString[i] {
	//		return false, krberror.NewErrorf(krberror.KRBMsgError, "SName in response does not match what was requested. Requested: %+v; Reply: %+v", tgsReq.ReqBody.SName, k.DecryptedEncPart.SName)
	//	}
	//}
	if .DecryptedEncPart.SRealm != .ReqBody.Realm {
		return false, krberror.NewErrorf(krberror.KRBMsgError, "SRealm in response does not match what was requested. Requested: %s; Reply: %s", .ReqBody.Realm, .DecryptedEncPart.SRealm)
	}
	if len(.DecryptedEncPart.CAddr) > 0 {
		if !types.HostAddressesEqual(.DecryptedEncPart.CAddr, .ReqBody.Addresses) {
			return false, krberror.NewErrorf(krberror.KRBMsgError, "addresses listed in the TGS_REP does not match those listed in the TGS_REQ")
		}
	}
	if time.Since(.DecryptedEncPart.StartTime) > .LibDefaults.Clockskew || .DecryptedEncPart.StartTime.Sub(time.Now().UTC()) > .LibDefaults.Clockskew {
		if time.Since(.DecryptedEncPart.AuthTime) > .LibDefaults.Clockskew || .DecryptedEncPart.AuthTime.Sub(time.Now().UTC()) > .LibDefaults.Clockskew {
			return false, krberror.NewErrorf(krberror.KRBMsgError, "clock skew with KDC too large. Greater than %v seconds.", .LibDefaults.Clockskew.Seconds())
		}
	}
	return true, nil
}